package edu.hawaii.duedates.lender;

import java.util.Calendar;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.Date;
import java.text.ParseException;
import java.text.SimpleDateFormat;

/**
 * Defines a generic borrowed item and a generic method of printing its contents and due date.
 * 
 * @author Aric West
 * @author Daniel Tian
 */
public class BorrowedItem implements Comparable<BorrowedItem> {
  private final String itemName;
  private final Calendar dueDate;
  
  /**
   * Constructor. Sets the name and due date of the borrowed item. The due date should be passed
   * as a string in the format MM-dd-yy or MM-dd-yyyy. The dashes can be substituted with either
   * slashes or dots. The month and day can be one or two digits.
   * 
   * @param itemName Name of borrowed item.
   * @param dueDate Due date of the item.
   * @throws BorrowedItemException If the item name or due date is null.
   */
  public BorrowedItem(String itemName, String dueDate) throws BorrowedItemException {
    if (itemName == null) {
      throw new BorrowedItemException("item name is null");
    }
    else if (dueDate == null) {
      throw new BorrowedItemException("date is null");
    }
    else {
      this.itemName = itemName;
      this.dueDate = Calendar.getInstance();
      
      String dateString;
      String regex = "(0?[1-9]|1[012])[/.-](0?[1-9]|[12][0-9]|3[01])[/.-]((19|20)?\\d\\d)";
      Pattern pattern = Pattern.compile(regex);
      Matcher matcher = pattern.matcher(dueDate);
      matcher.find();
      
      try {
        dateString = matcher.group().replaceAll("[/.]", "-");
        SimpleDateFormat dateFormat = new SimpleDateFormat("MM-dd-yy", Locale.US);
        
        Date date = dateFormat.parse(dateString);
        this.dueDate.setTime(date);
      }
      catch (IllegalStateException e) {
        throw new BorrowedItemException("no valid date found in the string \"" + dueDate + "\"", e);
      }
      catch (ParseException e) {
        // should not throw unless date format is changed
        throw new BorrowedItemException("error parsing date string to date object", e);
      }
    }
  }
  
  /**
   * Returns the name of the item.
   * 
   * @return The name of the item.
   */
  public String getItemName() {
    return this.itemName;
  }
  
  /**
   * Returns the due date of the item.
   * 
   * @return The due date of the item.
   */
  public Calendar getDueDate() {
    return this.dueDate;
  }
  
  /**
   * Returns the due date of the item as a string.
   * 
   * @return The due date of the item as a string.
   */
  public String getDueDateToString() {
    SimpleDateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy", Locale.US);
    return dateFormat.format(this.dueDate.getTime());
  }
  
  /**
   * Compares this object's date to another object's date. Used for sorting.
   * 
   * @param o The object to compare this object to.
   * @return The compareTo result.
   */
  public int compareTo(BorrowedItem o) {
    if (this.dueDate.compareTo(o.dueDate) == 0) {
      return (this.itemName.compareTo(o.itemName));
    }
    else {
      return this.dueDate.compareTo(o.dueDate);
    }
  }

  /**
   * Checks that a compared object is type BorrowedItem and that they have the same name, due date,
   * and lender. 
   * 
   * @param o object
   * @return boolean value
   */
  public boolean equals(Object o) {
    if (o == this) {
      return true;
    }
    if (!(o instanceof BorrowedItem)) {
      return false;
    }
    
    BorrowedItem item = (BorrowedItem)o;
    return item.dueDate.equals(this.dueDate) && item.itemName.equals(this.itemName);
  }

  /**
   * Calculates a hash value for this object.
   * 
   * @return A hash value based on the due date of the item.
   */
  public int hashCode() {
    int hash = 7;
    hash = 31 * hash + this.itemName.hashCode();
    hash = 31 * hash + this.dueDate.hashCode();
    
    return hash;
  }
  
  /**
   * Returns the item due date and name as a string.
   * 
   * @return A string of the due date and item name.
   */
  @Override
  public String toString() {
    return "Due: " + this.getDueDateToString() + " | " + this.itemName;
  }
}
